/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package gride.project;

import gride.GrideSettings;
import gride.exceptions.XMLFormatException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.swing.JOptionPane;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 *
 * @author lukasz
 */
public class Project
{

    // <editor-fold defaultstate="expanded" desc="Members">

    private String name;
    
    private File projectDir;
    private File sourceDir;
    private File buildDir;
    private File libDir;
    private File docDir;

    private GrideScript mainScript;

    private List<GrideScript> scripts;
    private List<GrideLibrary> libraries;
    
    // </editor-fold>
    


    // <editor-fold defaultstate="expanded" desc="Constructors">
    
    /**
     * Default constructor - takes the name of the project
     * @param name
     */
    public Project(String name)
    {
        this.name = name;
        
        // create project directories
        projectDir = new File(GrideSettings.PROJECTS_DIR + "/" + name);
        sourceDir = new File(projectDir.getPath() + "/" +
                             GrideSettings.SOURCE_DIR_EXT);
        buildDir = new File(projectDir.getPath() + "/" +
                            GrideSettings.BUILD_DIR_EXT);
        libDir = new File(projectDir.getPath() + "/" +
                          GrideSettings.LIB_DIR_EXT);
        docDir = new File(projectDir.getPath() + "/" +
                          GrideSettings.DOC_LIB_EXT);
        projectDir.mkdirs();
        sourceDir.mkdir();
        buildDir.mkdir();
        libDir.mkdir();
        docDir.mkdir();
        
        scripts = new ArrayList<GrideScript>();
        libraries = new ArrayList<GrideLibrary>();
    }


    /**
     * Loads a project from the given .gri file
     * @param file
     * @return loaded project
     * @throws java.lang.Exception
     */
    public static Project loadProject(File file) throws Exception
    {
        DocumentBuilder docBuilder =
                      DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document doc = docBuilder.parse(file);

        Element projectNode = doc.getDocumentElement();

        if (!projectNode.getNodeName().equals("Project"))
            throw new XMLFormatException("Wrong format of project XML file");

        Project project = new Project(projectNode.getAttribute("Name"));
        
        NodeList nodes = projectNode.getElementsByTagName("ProjectDir");
        if (nodes.getLength() > 0)
            project.setProjectDir(new File(nodes.item(0).getTextContent()));

        nodes = projectNode.getElementsByTagName("SourceDir");
        if (nodes.getLength() > 0)
            project.setSourceDir(new File(nodes.item(0).getTextContent()));

        nodes = projectNode.getElementsByTagName("BuildDir");
        if (nodes.getLength() > 0)
            project.setBuildDir(new File(nodes.item(0).getTextContent()));

        nodes = projectNode.getElementsByTagName("LibDir");
        if (nodes.getLength() > 0)
            project.setLibDir(new File(nodes.item(0).getTextContent()));

        nodes = projectNode.getElementsByTagName("DocDir");
        if (nodes.getLength() > 0)
            project.setDocDir(new File(nodes.item(0).getTextContent()));

        
        nodes = projectNode.getElementsByTagName("Scripts");
        if (nodes.getLength() > 0)
        {
            nodes = ((Element)nodes.item(0)).getElementsByTagName("Script");
            for (int i=0; i<nodes.getLength(); i++)
            {
                GrideScript script =
                        GrideScript.loadScript((Element)nodes.item(i));

                project.addScript(script);

                if (script.isMainScript())
                    project.setMainScript(script);
            }
        }
     
        nodes = projectNode.getElementsByTagName("Libraries");
        if (nodes.getLength() > 0)
        {
            nodes = ((Element)nodes.item(0)).getElementsByTagName("Library");
            for (int i=0; i<nodes.getLength(); i++)
                project.addLibrary(
                        GrideLibrary.loadLibrary((Element)nodes.item(i)));
        }

        return project;
    }


    // </editor-fold>



    // <editor-fold defaultstate="expanded" desc="Getters/Setters">

    /**
     * gets the project name
     * @return
     */
    public String getName()
    {
        return name;
    }

    /**
     * sets the project name
     * @param name
     */
    public void setName(String name)
    {
        this.name = name;
    }


    /**
     * gets the project directory
     * @return
     */
    public File getProjectDir()
    {
        return projectDir;
    }

    /**
     * sets the project directory
     * @param dir
     */
    public void setProjectDir(File dir)
    {
        this.projectDir = dir;
        this.projectDir.mkdirs();
    }


    /**
     * gets the project's source directory
     * @return
     */
    public File getSourceDir()
    {
        return sourceDir;
    }

    /**
     * sets the project's source directory
     * @param dir
     */
    public void setSourceDir(File dir)
    {
        this.sourceDir = dir;
        this.sourceDir.mkdirs();
    }


    /**
     * gets the project's build directory
     * @return
     */
    public File getBuildDir()
    {
        return buildDir;
    }

    /**
     * sets the project's build directory
     * @param dir
     */
    public void setBuildDir(File dir)
    {
        this.buildDir = dir;
        this.buildDir.mkdirs();
    }


    /**
     * gets the project's libraries directory
     * @return
     */
    public File getLibDir()
    {
        return libDir;
    }

    /**
     * sets the project's libraries directory
     * @param dir
     */
    public void setLibDir(File dir)
    {
        this.libDir = dir;
        this.libDir.mkdirs();
    }


    /**
     * gets the project's documentation directory
     * @return
     */
    public File getDocDir()
    {
        return docDir;
    }

    /**
     * sets the project's documentation directory
     * @param dir
     */
    public void setDocDir(File dir)
    {
        this.docDir = dir;
        this.docDir.mkdirs();
    }


    /**
     * gets the list of scripts in this project
     * @return
     */
    public List<GrideScript> getScripts()
    {
        return scripts;
    }

    /**
     * gets the list of libraries in this project
     * @return
     */
    public List<GrideLibrary> getLibraries()
    {
        return libraries;
    }

    /**
     * gets the mains script of this project
     * @return
     */
    public GrideScript getMainScript()
    {
        return mainScript;
    }

    /**
     * sets the main script of this project
     * @param mainScript
     */
    public void setMainScript(GrideScript mainScript)
    {
        if (mainScript != null)
            mainScript.setMainScript(true);

        if (this.mainScript != null)
            this.mainScript.setMainScript(false);

        this.mainScript = mainScript;
    }

    // </editor-fold>



    // <editor-fold defaultstate="expanded" desc="Public Methods">


    /**
     * adds a script to the project
     * @param newScript
     */
    public void addScript(GrideScript newScript)
    {
        
        scripts.add(newScript);
    }

    /**
     * removes a script from the project
     * @param s
     */
    public void excludeScript(GrideScript s)
    {
        scripts.remove(s);

        deleteFile(s.getSourcefile());
        deleteFile(s.getClassFile());

        if (mainScript == s)
            setMainScript(null);
    }

    /**
     * adds a library to the project
     * @param newLibrary
     */
    public void addLibrary(GrideLibrary newLibrary)
    {
        libraries.add(newLibrary);
    }

    /**
     * removes a library from the project
     * @param l
     */
    public void excludeLibrary(GrideLibrary l)
    {
        libraries.remove(l);

        deleteFile(l.getFile());
    }

    /**
     * tries to save the project, but in case of problems
     * intercepts the exception and returns as if nothing happened
     */
    public void trySaveProject()
    {
        try
        {
            saveProject();
        }
        catch (IOException ex)
        {
            System.out.println("Error: " + ex.getMessage());
        }
    }

    /**
     * saves the project
     * @throws java.io.IOException
     */
    public void saveProject() throws IOException
    {
        // make sure project directory is created
        projectDir.mkdirs();

        FileWriter writer = new FileWriter(projectDir.getAbsolutePath() + "/" +
                                           name + ".gri");

        writer.write(this.getXML());
        writer.close();
    }


    /**
     * gets a list of all packages of this project
     * @return
     */
    public List<String> getCompletePackageList()
    {
        return getScriptPackageList();
    }


    
    /**
     * returns a sorted list of packages amongst the scripts in this project
     * @return
     */
    public List<String> getScriptPackageList()
    {
        List<String> list = new ArrayList<String>();

        for (GrideScript s : scripts)
            if (!list.contains(s.getPackage()))
                list.add(s.getPackage());

        Collections.sort(list);
        return list;
    }


    
    /**
     * returns a sorted list of scripts in the given package
     * @param pkg
     * @return
     */
    public List<GrideScript> getScriptsInPackage(String pkg)
    {
        List<GrideScript> list = new ArrayList<GrideScript>();

        for (GrideScript s : scripts)
            if (s.getPackage().equals(pkg))
                list.add(s);

        Collections.sort(list, new Comparator()
        {
            public int compare(Object o1, Object o2)
            {
                return ((GrideScript)o1).getName().compareTo(
                                                   ((GrideScript)o2).getName());
            }
        });
        return list;
    }

    /**
     * gets the XML representing this project
     * @return
     */
    public String getXML()
    {
        StringBuilder xml = new StringBuilder("<?xml version=\"1.0\"?>\n");
        
        xml.append("<Project Name=\"" + name + "\">\n");
        
        xml.append("\t<ProjectDir>" + projectDir.getAbsolutePath() +
                   "</ProjectDir>\n");
        xml.append("\t<SourceDir>" + sourceDir.getAbsolutePath() +
                   "</SourceDir>\n");
        xml.append("\t<BuildDir>" + buildDir.getAbsolutePath() +
                   "</BuildDir>\n");
        xml.append("\t<LibDir>" + libDir.getAbsolutePath() + "</LibDir>\n");
        xml.append("\t<DocDir>" + docDir.getAbsolutePath() + "</DocDir>\n");

        xml.append("\n");

        xml.append("\t<Scripts>\n");
        for (GrideScript script : scripts)
            xml.append("\t\t" + script.getXML().replaceAll("\n", "\n\t\t") +
                       "\n");
        xml.append("\t</Scripts>\n");

        xml.append("\n");

        xml.append("\t<Libraries>\n");
        for (GrideLibrary library : libraries)
            xml.append("\t\t" + library.getXML().replaceAll("\n", "\n\t\t") +
                       "\n");
        xml.append("\t</Libraries>\n");
        
        xml.append("</Project>\n");
        
        return xml.toString();
    }

    // </editor-fold>

    // <editor-fold defaultstate="expanded" desc="Private Methods">

    private void deleteFile(File f)
    {
        if (f == null)
            return;

        try
        {
            f.delete();
        }
        catch (Exception e)
        {
            JOptionPane.showMessageDialog(null, "Couldn't delete the file!",
                                          "Error", JOptionPane.ERROR_MESSAGE);
        }
    }

    // </editor-fold>
}
